package com.lancoo.edu.platform.operation.commons.handler;

import com.lancoo.edu.platform.operation.commons.exception.ServiceException;
import com.lancoo.edu.platform.operation.commons.exception.TokenException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 全局异常捕捉
 *
 * @author pky
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 捕获数据校验异常
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, Object>> validationBodyException(MethodArgumentNotValidException e) {
        log.error(e.getMessage());
        BindingResult result = e.getBindingResult();
        return setValidationResult(result);
    }

    /**
     * 捕获数据绑定校验异常处理
     *
     * @param e 数据绑定校验异常
     * @return 错误响应结果
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<Map<String, Object>> requestBindException(BindException e) {
        log.error(e.getMessage());
        BindingResult result = e.getBindingResult();
        return setValidationResult(result);
    }

    /**
     * 设置数据校验返回结果
     *
     * @param result 错误结果详情
     * @return 错误响应结果
     */
    private ResponseEntity<Map<String, Object>> setValidationResult(BindingResult result) {
        // 封装错误消息为字符串
        StringBuilder sb = new StringBuilder();
        if (result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            for (int i = 0; i < errors.size(); i++) {
                FieldError fieldError = (FieldError) errors.get(i);
                String exMsg = fieldError.getDefaultMessage();
                if (i == errors.size() - 1 ){
                    if(StringUtils.isNotBlank(exMsg) && exMsg.contains("LocalDateTime")) {
                        sb.append(String.format("%s.%s", i + 1, fieldError.getField() + "格式不正确"));
                    }
                    else {
                        sb.append(String.format("%s.%s", i + 1, exMsg));
                    }
                } else {
                    if(StringUtils.isNotBlank(exMsg) && exMsg.contains("LocalDateTime")) {
                        sb.append(String.format("%s.%s", i + 1, fieldError.getField() + "格式不正确")).append(";");
                    }
                    else {
                        sb.append(String.format("%s.%s", i + 1, exMsg)).append(";");
                    }
                }
            }
        }
        Map<String, Object> map = setResultMap(400, sb.toString());
        return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
    }

    /**
     * 捕获参数转换错误
     *
     * @return 错误响应结果
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<Map<String, Object>> mismatchErrorHandler(MethodArgumentTypeMismatchException e) {
        log.error(e.getMessage());
        Map<String, Object> map = setResultMap(405, "参数类型不正确");
        return new ResponseEntity<>(map, HttpStatus.METHOD_NOT_ALLOWED);
    }


    /**
     * 捕获地址参数错误异常
     *
     * @return 错误响应结果
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public ResponseEntity<Map<String, Object>> missingPathVariableExceptionHandler(MissingPathVariableException e) {
        log.error(e.getMessage());
        Map<String, Object> map = setResultMap(400, "地址参数错误");
        return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
    }


    /**
     * 捕获url参数错误异常（此处只用于系统健康检测的异常捕获，对外提供的返回，不可改动）
     *
     * @return 错误响应结果
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity<Map<String, Object>> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException e) {
        log.error(e.getMessage());
        Map<String, Object> map = setHealthResultMap(3, "参数错误");
        return new ResponseEntity<>(map, HttpStatus.OK);
    }


    /**
     * 捕获请求方式不正确异常处理
     *
     * @param e 请求方式不正确异常
     * @return 错误响应结果
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<Map<String, Object>> requestMethodException(HttpRequestMethodNotSupportedException e) {
        log.error(e.getMessage());
        Map<String, Object> map = setResultMap(405, "请求方式不正确");
        return new ResponseEntity<>(map, HttpStatus.METHOD_NOT_ALLOWED);
    }

    /**
     * 捕获参数类型不正确异常处理
     *
     * @param e 参数类型不正确异常
     * @return 错误响应结果
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ResponseEntity<Map<String, Object>> mediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        log.error(e.getMessage());
        Map<String, Object> map = setResultMap(400, "参数类型不正确");
        return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
    }

    /**
     * 捕获参数类型不正确异常处理
     *
     * @param e 参数类型不正确异常
     * @return 错误响应结果
     */
    @ExceptionHandler(HttpMessageConversionException.class)
    public ResponseEntity<Map<String, Object>> messageConversionException(HttpMessageConversionException e) {
        String message = e.getMessage();
        String param = "";
        if (StringUtils.isNotBlank(message)) {
            if(message.contains("request body is missing")) {
                param = ": 请求体缺失";
            }
            else if(message.contains("JSON parse error")) {
                param = ": " + StringUtils.substringBetween(message, "[\"", "\"]") + " JSON 格式错误";
            }
            else {
                param = ": " + StringUtils.substringBetween(message, "[\"", "\"]");
            }
        }
        Map<String, Object> map = setResultMap(400, "参数类型不正确" + param);
        return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
    }

    /**
     * 捕获文件大小超出限制异常处理
     *
     * @param e 参数类型不正确异常
     * @return 错误响应结果
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ResponseEntity<Map<String, Object>> messageConversionException(MaxUploadSizeExceededException e) {
        log.error(e.getMessage());
        Map<String, Object> map = setResultMap(400, "文件大小超出限制");
        return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
    }

    /**
     * 捕获自定义访问异常处理
     *
     * @param e Token 认证异常
     * @return 错误响应结果
     */
    @ExceptionHandler(TokenException.class)
    public ResponseEntity<Map<String, Object>> tokenException(TokenException e) {
        log.error(e.getMessage());
        Map<String, Object> map = setResultMap(401, "token 认证失败");
        return new ResponseEntity<>(map, HttpStatus.UNAUTHORIZED);
    }

    /**
     * 捕获自定义业务逻辑异常(此处是自定义异常响应结果，不可改动)
     *
     * @param exception 业务逻辑异常
     * @return 错误响应结果
     */
    @ExceptionHandler(ServiceException.class)
    public ResponseEntity<Map<String, Object>> serviceException(ServiceException exception) {
        Map<String, Object> map = setResultMap(400, exception.getMessage());
        return new ResponseEntity<>(map, HttpStatus.OK);
    }

    /**
     * 捕获全局异常
     *
     * @param e 全局异常
     * @return 错误响应结果
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> globalException(Exception e) {
        Map<String, Object> map = setResultMap(500, "服务器异常，请联系管理员：" + e.getMessage());
        e.printStackTrace();
        log.error("未知异常"+e.getMessage(),e);
        return new ResponseEntity<>(map, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * 设置返回状态码和信息
     *
     * @param code 状态码
     * @param msg  信息
     * @return 错误响应结果
     */
    private Map<String, Object> setResultMap(Integer code, Object msg) {
        Map<String, Object> map = new HashMap<>(16);
        map.put("code", code);
        map.put("msg", msg);
        return map;
    }

    /**
     * 设置系统健康检测返回状态码和信息
     *
     * @param error 状态码
     * @param msg   信息
     * @return 错误响应结果
     */
    private Map<String, Object> setHealthResultMap(Integer error, String msg) {
        Map<String, Object> map = new HashMap<>(16);
        map.put("error", error);
        map.put("msg", msg);
        return map;
    }

}
